#!/usr/bin/env python2
#-*- coding: utf-8 -*-
import re
import os
import sys
import stat
import socket
import inspect
import logging
import logging.handlers
import traceback
import time
import datetime
import getopt
import paramiko
from paramiko.client import SSHException

from socket import error as socket_error

from Ump.common.log import log_init
from Ump.common.exception import LichFault, AuthenticationFailed, LichLiscenFault
from subprocess import Popen, PIPE
from Ump.common.utils import package_error

LOG = logging.getLogger('Ump.common.remote')


def _session_recv(session):
    try:
        data = session.recv(4096)
    except socket.timeout as err:
        data = ""

    return data

def _session_recv_stderr(session):
    try:
        data = session.recv_stderr(4096)
    except socket.timeout as err:
        data = ""

    return data

def _exec_remote(hostname, cmd, username='root', password='mdsmds', \
                timeout=301, iscode=True, is_print=False):

    """
    :param iscode: to skip return code check;
    :return: 
    """
    if is_print:
        print hostname, cmd
    try:
        remote_addr = web.ctx.env.get('REMOTE_ADDR')
        web_username =  web.ctx.session.user 
    except Exception,e:
        remote_addr = '127.0.0.1'
        web_username = 'autosync@'

    client = paramiko.client.SSHClient()
    client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())   
    host = hostname
    try:
        client.connect(hostname = host,username=username, password=password,timeout=3)   
    except socket.timeout as err:
        raise Exception("主机连接失败,网络不通或是关机状态")
    except socket.error as err:
        package_error(1,str(err),cmd)
        raise Exception(err)
    except paramiko.ssh_exception.AuthenticationException as err:
        raise Exception("密码错误，认证失败")
    except socket_error, e:
        package_error(1,str(e),cmd)
        raise Exception(e)
    except Exception,e:
        raise Exception('SSH Client is Failed,%s'%e)

    timeout = timeout
    if cmd.find('allocate') != -1 or cmd.find('node.py --disk_add') != -1:
        timeout = 6*60*60
    
    stdout = ""
    stderr = ""
    recode = 0
    transport = client.get_transport()
    session = transport.open_channel(kind='session') 

    try:
        session.settimeout(3)
        session.exec_command(str(cmd))
        start_time = datetime.datetime.now()
        delta = datetime.timedelta(seconds=timeout)
    
        while True:
            now_time = datetime.datetime.now()
            if now_time - start_time > delta:
                raise Exception('命令执行超时')

            if session.recv_ready():
                data = _session_recv(session)
                stdout = stdout + data
    
            if session.recv_stderr_ready():
                data = _session_recv_stderr(session)
                stderr = stderr + data
    
            if session.exit_status_ready():
                while True:
                    data = _session_recv(session)
                    if data == "":
                        break
                    stdout = stdout + data
    
                while True:
                    data = _session_recv_stderr(session)
                    if data == "":
                        break
                    stderr = stderr + data
    
                break
    
        recode = session.recv_exit_status()
    except Exception,e:
        client.close()
        if str(e).find("Channel closed") != -1:
            raise Exception("远程执行命令出错，协议通道异常关闭,请重试解决")
        package_error(1,str(e),cmd)
        raise Exception(e)
    finally:
        session.close()
        client.close()

    if not cmd == 'ls':
        LOG.info('from %s to %s-by-%s -%s -- errcode-%s\n%s'%(remote_addr, hostname,web_username,cmd,recode,stdout.strip()))

    if ('license' in cmd and 'sniffer' in cmd):
        stdout = stdout + '\n' + stderr
        return stdout

    if not iscode:
        return True

    if not stderr == '':
        LOG.error('%s\n%s'%(cmd,stderr.strip()))

    if recode != 0:
        if recode < 256 and recode > 133 and not stderr.strip():
            raise LichFault('主机连接中断,网络中断或是正在关机')

        package_error(recode,stderr,cmd)
        raise LichFault('%s'%stderr.strip())
            
    if 'listnode' in cmd:
        stdout = stdout + '\n' + stderr

    return stdout 

def _put_remote(hostname, local_dir, remote_dir, username='root', password='mdsmds', ischmod=False):
    s = paramiko.client.SSHClient()
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    try:
        s.connect(hostname, 22, username, password, timeout = 3)
        sftp = s.open_sftp()
        try:
            sftp.mkdir(os.path.dirname(remote_dir))
        except: pass
        sftp.put(local_dir, remote_dir)
        if ischmod:
            sftp.chmod(remote_dir, 755)
    except Exception,e:
        LOG.error('免密码登录设置失败,远程传输公钥文件出错--%s'%(e))
        raise Exception('免密码登录设置失败,远程传输公钥文件出错')
    finally:
        s.close()

def _get_remote(hostname, local_dir, remote_dir, username='root', password='mdsmds'):
    '''download from remote host'''
    port = 22
    s = paramiko.client.SSHClient()
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    try:
        s.connect(hostname, 22, username, password, timeout = 3)
        sftp = s.open_sftp()
        files=sftp.listdir(remote_dir)
        for f in files:
            sftp.get(os.path.join(remote_dir,f),os.path.join(local_dir,f))#下载
        sftp.close()
    except Exception,e:
        traceback.print_exc()
        LOG.info("connect error!")

def _check_remote_file_exists(hostname, cmd, username='root',password='mdsmds'):
    '''check remote file exists'''
    result = []
    port = 22
    s = paramiko.client.SSHClient()
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    try:
        s.connect(hostname, 22, username, password, timeout = 3)
        sftp = s.open_sftp()
        ret, stdout, stderr = sftp.exec_command(cmd)
        print 'ddddddddddddddddddddd' , os.getcwd()
        if not os.path.exists('qos_maxbandwidth'):
            result.append('max')
        if not os.path.exists('qos_policy'):
            result.append('pol')
        sftp.close()
    except Exception,e:
        traceback.print_exc()
        LOG.info("connect error!")
    return  result

def _get_remote_file(hostname, local_file, remote_file,username='root',password='mdsmds'):

    '''download from remote host'''
    port = 22
    s = paramiko.client.SSHClient()
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    try:
        s.connect(hostname, 22, username, password, timeout = 3)
        sftp = s.open_sftp()
        sftp.get(remote_file, local_file)
        sftp.close()
    except Exception,e:
        raise Exception('download file fail')
        return False
    finally:
        s.close()

def get_all_files_in_remote_dir(sftp, remote_dir):
    all_files = list()
    if remote_dir[-1] == '/':
        remote_dir = remote_dir[0:-1]
    
    files = sftp.listdir_attr(remote_dir)
    for remote_file in files:
        filename = remote_dir + '/' + remote_file.filename
        if stat.S_ISDIR(remote_file.st_mode):
            all_files.extend(get_all_files_in_remote_dir(sftp, filename))
        else:
            all_files.append(filename)
    return all_files
    
def _dir_get_remote(hostname, local_dir, remote_dir, username='root', password='mdsmds', port=22, ischmod=False):
    s = paramiko.client.SSHClient()
    
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    try:
        s.connect(hostname, port, username, password, timeout = 3)
        sftp = s.open_sftp()
        all_files = get_all_files_in_remote_dir(sftp, remote_dir)
        for remote_file_path in all_files:
            filename = remote_file_path.split('/')[-1]
            local_filename = os.path.join(local_dir, filename)
            sftp.get(remote_file_path, local_filename)
        sftp.close()
    except Exception,e:
        LOG.info(e)
        LOG.error('远程下载目录错误--%s'%(e))
        raise Exception('远程下载目录错误')
    finally:
        s.close()
    return True

def _dir_put_remote(hostname, local_dir, remote_dir, username='root', password='mdsmds', port=22, ischmod=False):
    s = paramiko.client.SSHClient()
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
    def remote_mkdir(): 
        remote_dirs = remote_dir.split(os.sep)
        path = os.sep 
        for dir_ in remote_dirs:
            if not dir_:
                continue
            path = os.path.join(path, dir_)
            try:
                sftp.mkdir(path)
            except: pass
    try:
        s.connect(hostname, port, username, password, timeout = 3)
        sftp = s.open_sftp()
        remote_mkdir() 
        for root, dirs, files in os.walk(local_dir):
            for dirname in dirs:
                local_path = os.path.join(root, dirname)
                prefix_path = local_path.replace(local_dir, '').lstrip(os.sep)
                remote_path = os.path.join(remote_dir, prefix_path)
                try:
                    sftp.mkdir(remote_path)
                except Exception,e:
                    LOG.info('WARNING:remote mkdir error : %s' % e)
                    pass
            for filename in files:
                local_file = os.path.join(root, filename)
                prefix_path = local_file.replace(local_dir, '').lstrip(os.sep)
                remote_file = os.path.join(remote_dir, prefix_path)
                try:
                    sftp.put(local_file, remote_file)
                except Exception,e:
#                    LOG.info(e,remote_file,'===')
                    sftp.mkdir(os.path.dirname(remote_file))
                    sftp.put(local_file,remote_file)
                if ischmod:
                    sftp.chmod(remote_file,755)

        sftp.close()
    except Exception,e:
        LOG.info(e)
        LOG.error('远程上传目录错误--%s'%(e))
        raise Exception('远程上传目录错误')
    finally:
        s.close()
    return True

def _check_authentication(hostname,username='root',password='mdsmds',port = 22):
    s=paramiko.client.SSHClient()   
    s.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())   
    try:
        s.connect(hostname = hostname,username=username, password=password,timeout=3)   
    except socket.timeout as err:
        raise Exception("主机连接失败,网络不通或是关机状态")
    except Exception,e:
        raise Exception(e)

    home = os.path.split(inspect.getabsfile(_check_authentication))[0]
    os.system("touch %s/authentication"%(home))
    local_dir = home + '/authentication'
    remote_dir = '/tmp/stor_authentication'
    s=paramiko.transport.Transport((hostname,port))   
    try:
        s.connect(username=username,password=password)   
    except paramiko.ssh_exception.AuthenticationException as err:
        raise Exception("密码错误，认证失败")

    sftp=paramiko.sftp_client.SFTPClient.from_transport(s)   
    sftp.put(local_dir,remote_dir)   
    s.close()
    return True

def _get_value(path):
    size = os.path.getsize(path)
    fd = open(path, 'r')
    buf = fd.read(size)
    fd.close()
    return buf

def _ssh_key(hostname, passwd):
    while (1):
        os.system("if [ ! -f  /root/.ssh/id_rsa.pub ];then ssh-keygen -t rsa -P '' -f /root/.ssh/id_rsa ; fi")
        pub = _get_value('/root/.ssh/id_rsa.pub')
        private = _get_value('/root/.ssh/id_rsa')
        pub_dir = '/root/.ssh/id_rsa.pub'
        remote_dir = '/tmp/id_rsa.pub'
        _put_remote(hostname, pub_dir, remote_dir, password=passwd)
        if "'" in pub or '"' in pub or "'" in private or '"' in private :
            os.system('rm -rf /root/.ssh/id_rsa*')
        else:
            break

    try:
        cmd = "if [ ! -d /root/.ssh ]; then mkdir /root/.ssh; fi"
        cmd = cmd + " && cat '%s' >> /root/.ssh/authorized_keys" % (remote_dir)
        _exec_remote(hostname, cmd, 'root', passwd)
    except Exception,e:
        raise Exception(e)

def ssh_key(hostname, passwd):
    os.system("cat /root/.ssh/known_hosts > /tmp/known_hosts")

    cmd = "if [ ! -d /root/.ssh ]; then mkdir /root/.ssh; fi"
    cmd = cmd + "&& if [ ! -f  /root/.ssh/known_hosts ];then touch /root/.ssh/known_hosts ; fi"
    _exec_remote(hostname, cmd, 'root', passwd)
    _exec_remote(hostname, 'rm -f /tmp/known_hosts', 'root', passwd)
    _ssh_key(hostname, passwd)
    LOG.info('OK')
    return True

if __name__ == '__main__':
    local_dir = '/home/mingpeng/git/fusionstor/ump/Ump/lichtools/'
    cmd = 'xxx;hostname'
    print _exec_remote('192.168.120.73', cmd)
